home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Text⁄Files / Suntar 1.3.2 / printf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-18  |  12.0 KB  |  439 lines  |  [TEXT/KAHL]

  1. /*******************************************************************************\
  2.  
  3. printf module
  4.  
  5. part of suntar, ©1991,1992 Sauro & Gabriele Speranza
  6.  
  7. This program is public domain, feel free to use it or part of it for anything
  8.  
  9. \*******************************************************************************/
  10.  
  11. /* it's a not 100% standard version of printf: it does not support field
  12. length (%5d is NOT allowed) and has a new descriptor %P for Pascal strings
  13.  
  14. However, it has a sophisticated handling of buffering and flushing
  15. */
  16.  
  17. #define WIN_INDEX 0
  18. /* I found no interest in supporting windows other then window 0, but
  19. if you place the buffer and some flags in the window struct it would be
  20. easy to adapt it to write to any window; the easiest way is to use a variable
  21. as WIN_INDEX, obviously flushing the buffer every time it's changed
  22. */
  23.  
  24. #define FILE_PRINT
  25. #include "windows.h"
  26. #include "suntar.h"
  27.  
  28. static Boolean first_time=true;
  29. static char no_flush = 0;
  30. static char last_char=CR;
  31. short list_buf_size;
  32.  
  33. void my_flush(Boolean);
  34. void printdec(long);
  35. void print_oct(long);
  36. void printhex(long,Boolean);
  37. void printf(char*,int);
  38. void new_console(void);
  39.  
  40. static void new_console()
  41. {
  42.     /* create the window: since no older window exists, new_window will
  43.     use the index 0 (guessing the index is not good programming style, but
  44.     since suntar uses only one window it works) */
  45.     GrafPtr savePort;
  46.     GetPort( &savePort );
  47.     last_char=CR;
  48.     new_window();
  49.     first_time=false;
  50.     SetPort( &my_windows[WIN_INDEX] );
  51.     TEAutoView (true,my_windows[WIN_INDEX].TEH);
  52.     SetPort( savePort );
  53. }
  54.  
  55.  
  56. void disable_autoflush(mode)
  57. short mode;        /*    1: does not flush at the end of a printf, but only when the
  58.                         buffer is almost full (and the last character is a newline)
  59.                     2: as above, and it does not scroll the window as
  60.                         part of the flush */
  61. {
  62. /* the printf routine updates the screen, adjusts the scroll bars and
  63. scrolls the window so that the cursor is in sight, but
  64. a) that takes a lot of time
  65. b) if the printed lines are larger than the window, that may yield a lot of 
  66.     silly horizontal scrolling
  67. Hence, in the most critical sections of suntar updates are delayed and 
  68. scrolls are disabled.
  69. */
  70. if(first_time)    /* i.e., if no console window still exists... */
  71.     new_console();
  72. no_flush=mode;
  73. }
  74.  
  75. void enable_autoflush()
  76. {
  77.     no_flush=0;
  78.     flush_console();
  79. }
  80.  
  81. void flush_console()
  82. {
  83.     GrafPtr savePort;
  84.  
  85.     if(!my_windows[WIN_INDEX].used) return;
  86.     GetPort( &savePort );
  87.     SetPort( &my_windows[WIN_INDEX] );
  88.     my_flush(true);
  89.  
  90.     if( last_char==CR||last_char==LF){        /* to avoid horizontal scrolling, don't scroll
  91.             to show the current cursor position if it's not at beginning of line */
  92.         update_console();
  93.         TESelView( my_windows[WIN_INDEX].TEH);
  94.         }
  95.     if(curr_window==&my_windows[WIN_INDEX]){
  96.         SCR_BAR_AND_TEXT_IN_SYNC
  97.         }
  98. /* else 
  99.     per ora la scrollbar è comunque bianca, non importa aggiornarla, poi ci pensa 
  100.     MainEvent al momento dell'evento di attivazione
  101.     -- the scrollbar is currently white, it will be updated by MainEvent at the
  102.     activate event */
  103.  
  104.     SetPort( savePort );
  105. }
  106.  
  107.  
  108. #ifndef SUNTAR
  109. /* suntar does not use it, preferring the more powerful prompt(), but it was
  110. tested and debugged, so you may use it if you need it: it's a full implementation
  111. of the standard gets function, allowing all Mac-style editing tools on the
  112. line which is being typed */
  113. char * gets(buf)
  114. char*buf;
  115. {
  116. short io_start;
  117.  
  118. /* The window was read-only: now, it's broken in two parts, the bottom
  119. becomes read/write, but any existing text, up to the prompt which
  120. probably you printf-ed just before calling gets, remains read-only;
  121. furthermore, the read-write portion is one line (even Paste is altered
  122. so that you can't paste a newline character) */
  123. flush_all();
  124. io_start=my_windows[WIN_INDEX].lastPrompt=(**my_windows[WIN_INDEX].TEH).selEnd;
  125.  
  126. /* wait until the user types a return or enter */
  127. do{
  128.     MainEvent();
  129.     }
  130. while(my_windows[WIN_INDEX].lastPrompt!=32767);
  131. /* set the window as read-only again, and copy what appears after the prompt 
  132. in the output string */
  133. {register char *p= buf;
  134. register char *q= *((**my_windows[WIN_INDEX].TEH).hText) +io_start;
  135. register short i=(**my_windows[WIN_INDEX].TEH).teLength-io_start;
  136. while(i--)
  137.     *p++=*q++;
  138. *--p='\0';    /* il '\n' non va ritornato... */
  139. return buf;
  140. }
  141. }
  142. #endif
  143.  
  144. void start_of_line()
  145. {
  146. /* guarantees that the last character in the window is a carriage return */
  147. if(last_char!=CR&&last_char!=LF)
  148.     put_char(CR);
  149. }
  150.  
  151. void one_empty_line()
  152. /* guarantees that the last two characters in the window are carriage returns */
  153. {
  154. if(last_char!=CR){
  155.     put_char(CR);
  156.     if(last_char!=CR)        /* could be LF now... */
  157.         put_char(CR);
  158.     }
  159. }
  160.  
  161.  
  162. void prompt(buf,nbytes)
  163. /* come gets, ma ha un controllo di overflow e un valore iniziale
  164. -- similar to gets, but it has a buffer size parameter and the
  165. text input line has an initial value, which is selected so that you
  166. may delete it by typing any character
  167. */
  168. char*buf;
  169. short nbytes;
  170. {
  171. short io_start;
  172. GrafPtr savePort;
  173.  
  174. io_start=(**my_windows[WIN_INDEX].TEH).selEnd;
  175. if(buf[0]){
  176. #if 1    /* now Think C is more rigorous on type checks, and there is no way to match
  177.         the parameters ( '...' in the prototype is NOT compatible with the use of
  178.         vararg ! I have not tried with stdarg ).
  179.         In such cases, the normal solution would be to compile this function with
  180.         less type checking, but in Think C such flags are global, they can't be
  181.         set/reset on part of the project */
  182.     register char*p=buf;
  183.     while(*p) put_char(*p++);
  184.     put_char(' ');    /* the space seems superfluous, but without it the whole
  185.             line appears selected, and that's not very pretty to be seen */
  186. #else
  187.     printf("%s ",buf);
  188. #endif
  189.     flush_all();
  190.     }
  191. GetPort( &savePort );
  192. SetPort(&my_windows[WIN_INDEX]);
  193. TESetSelect((long)io_start,(**my_windows[WIN_INDEX].TEH).selEnd-(buf[0]!=0),my_windows[WIN_INDEX].TEH);
  194. update_console();
  195. TESelView( my_windows[WIN_INDEX].TEH);
  196. SetPort(savePort);
  197. if(curr_window != &my_windows[WIN_INDEX])    /* potrebbe esserci un desk accessory */
  198.     SelectWindow(&my_windows[WIN_INDEX]);
  199.  
  200. my_windows[WIN_INDEX].lastPrompt=io_start;
  201.  
  202. do{
  203.     MainEvent();
  204.     if(is_abort_command()){
  205.         GetPort( &savePort );
  206.         SetPort(&my_windows[WIN_INDEX]);
  207.         TESetSelect((long)io_start,(long)32767,my_windows[WIN_INDEX].TEH);
  208.         TEDelete(TEH);
  209.         my_windows[WIN_INDEX].lastPrompt=32767;        /* rimettilo readonly */
  210.         SetPort(savePort);
  211.         accept_abort_command();
  212.         }
  213.     }
  214. while(my_windows[WIN_INDEX].lastPrompt!=32767);
  215.  
  216. {register char *p= buf;
  217. register char *q= *((**my_windows[WIN_INDEX].TEH).hText) +io_start;
  218. register short i=(**my_windows[WIN_INDEX].TEH).teLength-io_start;
  219. if(i>nbytes) i=nbytes;
  220. while(i--)
  221.     *p++=*q++;
  222. *--p='\0';    /* il '\n' non va ritornato... */
  223. }
  224. }
  225.  
  226. #include <varargs.h>
  227.  
  228. void printf(fmt,va_alist)
  229. char*fmt;
  230. va_dcl
  231. {
  232. va_list ap;
  233. register char*format=fmt;
  234. long val;
  235. register char*string;
  236. short longform;
  237.  
  238.  
  239.     if(first_time) new_console();
  240.  
  241.     va_start(ap);
  242.     while(*format!='\0'){
  243.         if(*format!='%')
  244.             put_char(*format);
  245.         else{
  246.             format++;
  247.             if(longform = *format=='l')
  248.                 format++;
  249.             switch(*format){
  250.             case '%':
  251.                 put_char('%');
  252.                 break;
  253.             case 'D':
  254.                 longform=1; /* e prosegui...*/
  255.             case 'd':
  256.                 if(!longform)
  257.                     val= (long) va_arg(ap,short);
  258.                 else
  259.                     val= va_arg(ap,long);
  260.                 printdec(val);
  261.                 break;
  262.             case 'u':
  263.                 val= (long) va_arg(ap,unsigned short);    /* I've never used %lu... */
  264.                 printdec(val);
  265.                 break;
  266.             case 'X':
  267.                 longform=1; /* e prosegui...*/
  268.             case 'x':
  269.                 if(!longform)
  270.                     val= (long) va_arg(ap,short);
  271.                 else
  272.                     val= va_arg(ap,long);
  273.                 printhex(val,longform);
  274.                 break;
  275.             case 'o':
  276.                 if(!longform)
  277.                     val= (long) va_arg(ap,short);
  278.                 else
  279.                     val= va_arg(ap,long);
  280.                 print_oct(val);
  281.                 break;
  282.             case 'c':
  283.                 put_char( va_arg(ap,short) );
  284.                 break;
  285.             case 's':
  286.                 string= va_arg(ap,char*);
  287.                 while(*string)
  288.                     put_char(*string++);
  289.                 break;
  290.             case 'P':
  291. /* è un formato non standard C, ma mi fa molto comodo: una stringa formato Pascal
  292. -- a non-standard C format: a Pascal string, extremely useful on the Mac
  293. */
  294.                 string= va_arg(ap,char*);
  295.                 val= *string++;
  296.                 while(--val>=0)
  297.                     put_char(*string++);
  298.                 break;
  299.             default:
  300.                 SysBeep(5);    /* invalid format descriptor */
  301.             }
  302.             }
  303.             format++;
  304.         }
  305.     va_end(ap);
  306.     if(!no_flush)
  307.         flush_console();
  308. }
  309.  
  310. static void printdec(val)
  311. register long val;
  312. {
  313. char buf[14];
  314. register short i=-1;
  315. if(val<0){
  316.     val= -val;
  317.     put_char('-');
  318.     }
  319. do{
  320.     buf[++i]='0'+(short)(val%10);
  321.     val /= 10;
  322.     }
  323. while(val>0);
  324. while(i>=0) put_char(buf[i--]);
  325.  
  326. }
  327.  
  328. static void print_oct(val)
  329. register long val;
  330. {
  331. char buf[14];
  332. register short i=-1;
  333. do{
  334.     buf[++i]='0'+(short)(val&7);
  335.     val >>=3;
  336.     }
  337. while(val>0);
  338. while(i>=0) put_char(buf[i--]);
  339. }
  340.  
  341. static void printhex(val,longform)
  342. register long val;
  343. Boolean longform;
  344. {
  345. short digit,i=longform?28:12;
  346. short non_in_testa=0;
  347. while(i>=0){
  348.     if(!i) non_in_testa=1;
  349.     digit= (val>>i) & 0xF;
  350.     if(digit!=0 || non_in_testa){
  351.         put_char (digit<=9 ? '0'+digit : 'A'-10+digit );
  352.         non_in_testa=1;
  353.         }
  354.     i-=4;
  355.     }
  356. }
  357.  
  358.  
  359. char *printf_buf;
  360. static short chars_in_buffer=0;
  361.  
  362. void put_char(c)
  363. char c;
  364. {
  365. if(c==LF) c=CR;
  366. if(c!= CR || last_char==LF || last_char==CR )
  367.     last_char=c;
  368. else
  369.     last_char=LF;        /* meaning one carriage return but not two, CR means
  370.                         at least two carriage returns */
  371. printf_buf[chars_in_buffer++]=c;
  372. if(chars_in_buffer>=list_buf_size || chars_in_buffer>=list_buf_size-64 && c==CR ){
  373.     GrafPtr savePort;
  374.     GetPort( &savePort );
  375.     SetPort( &my_windows[WIN_INDEX]);
  376.     my_flush(no_flush<2);
  377.     SetPort( savePort );
  378.     }
  379. }
  380.  
  381. void update_console()
  382. {
  383. /* per uno stupido bug: il TextEdit non è pensato per scrivere su una finestra
  384. che sta in background e lo scroll (almeno quello automatico) non provvede a scrollare
  385. anche l'update region, col che l'update successivo è sulla posizione vecchia,
  386. non scrollata, del buco bianco. Unica soluzione, fare un update subito... ovviamente,
  387. per essere sicuri bisognerebbe chiamarla in continuazione, io mi limito a chiamarla
  388. quando chiudo una finestra di dialogo, quando ci sono forti probabilità di
  389. provocare un update
  390. -- TextEdit has a bad bug: the TESelView routine does scroll the text,
  391. but the update region is not scrolled (that is, OffsetRgn is not called) to 
  392. remember that the "white hole" left by an old window now closed was moved. 
  393. Hence, when the update event arrives it updates part of the window which needn't 
  394. an update and leaves part of the white hole. 
  395.  Only solution by now: don't call TeSelView when the update region is not empty:
  396. that is, either don't use TextEdit to implement the printf function (as a text
  397. editor it's still good, unless you type so quickly that a KeyDown event arrives 
  398. between the window closing and the update) or do your own implementation of TESelView 
  399. or call this function just before calling TESelView
  400. */
  401. if(!my_windows[WIN_INDEX].used)
  402.     return;
  403. else{
  404.     RgnHandle h=((WindowPeek)&my_windows[WIN_INDEX])->updateRgn;
  405.     RgnPtr p;
  406.     if(h && (p= *h) && p->rgnSize>=10)
  407.         if( *((long*)&p->rgnBBox) != *((long*)&p->rgnBBox.bottom) ) /* this test
  408.                 is redundant, but it may avoid a relatively expensive toolbox call */
  409.             if(!EmptyRgn (h)) UpdateWindow(&my_windows[WIN_INDEX]);
  410.     }    
  411. }
  412.  
  413.  
  414. static void my_flush(do_scroll)
  415. Boolean do_scroll;
  416. {
  417. if(chars_in_buffer){
  418.     /* window 0 is the console window, and old text must be deleted if the
  419.     TextEdit limit of 32 kbytes is approached... */
  420.     if( ((**my_windows[0].TEH).teLength >maxTElength-4000 ||
  421.         /* according to TN 237, there is another limit to the size of a TextEdit
  422.         record: the destination rect must not be taller than 32767 pixels: 
  423.         that limit is more stringent only if the line height in pixels is bigger
  424.         than the average line length in characters, a very rare situation,
  425.         unless you set the font size to 36 or 48, or use a small autowrap window  */
  426.         (**my_windows[0].TEH).nLines > 32000/(**my_windows[0].TEH).lineHeight) )
  427.             SilentSuppression(50);
  428.  
  429.     TEAutoView (false,my_windows[WIN_INDEX].TEH);    /* otherwise, a lot of horizontal scrolling...*/
  430.     TESetSelect((long)32767,(long)32767,my_windows[WIN_INDEX].TEH);
  431.     TEInsert(printf_buf,(long)chars_in_buffer,my_windows[WIN_INDEX].TEH);
  432.     TEAutoView (true,my_windows[WIN_INDEX].TEH);
  433.     if(do_scroll && (last_char==CR||last_char==LF) ){
  434.         update_console();
  435.         TESelView( my_windows[WIN_INDEX].TEH);
  436.         }
  437.     chars_in_buffer=0;
  438.     }
  439. }